<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>image-rota - Layer Documentation</title>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; line-height: 1.6; }
        .header { border-bottom: 2px solid #eee; padding-bottom: 20px; margin-bottom: 30px; }
        .section { margin-bottom: 30px; }
        .badge { display: inline-block; background: #007acc; color: white; padding: 2px 8px; border-radius: 3px; font-size: 12px; margin-right: 10px; }
        .policy-immediate { background: #28a745; color: white; text-decoration: none; }
        .policy-lazy { background: #ffc107; color: #212529; text-decoration: none; }
        .policy-force { background: #dc3545; color: white; text-decoration: none; }
        .policy-skip { background: #6c757d; color: white; text-decoration: none; }
        .policy-immediate:hover { background: #1e7e34; }
        .policy-lazy:hover { background: #e0a800; }
        .policy-force:hover { background: #c82333; }
        .policy-skip:hover { background: #545b62; }
        table { width: 100%; border-collapse: collapse; margin-top: 10px; table-layout: auto; }
        th, td { text-align: left; padding: 8px 12px; border-bottom: 1px solid #ddd; }
        th { background: #f8f9fa; font-weight: 600; }
        td:nth-child(3) {
            width: auto;
        }
        code { background: #f1f3f4; padding: 2px 4px; border-radius: 3px; font-family: 'Monaco', monospace; font-size: 14px; }
        code.long-default { word-break: break-all; white-space: pre-wrap; display: block; min-width: 30ch; }
        .back-link { margin-bottom: 20px; }
        .back-link a { text-decoration: none; color: #007acc; }
        .deps { display: flex; flex-wrap: wrap; gap: 5px; }
        .dep-badge { background: #28a745; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px; text-decoration: none; }
        .dep-badge:hover { background: #1e7e34; }
        /* Main content headers styling */
        h1, h2, h3, h4, h5, h6 { color: #333; margin-top: 20px; margin-bottom: 10px; }
        /* Companion documentation content styling */
        .companion-content h1, .companion-content h2, .companion-content h3, .companion-content h4, .companion-content h5, .companion-content h6 { color: #333; margin-top: 20px; margin-bottom: 10px; }
        .companion-content p { margin: 10px 0; }
        .companion-content ul, .companion-content ol { margin: 10px 0; padding-left: 30px; }
        .companion-content blockquote { border-left: 4px solid #007acc; padding-left: 15px; margin: 15px 0; color: #666; background: #f8f9fa; padding: 10px 15px; }
        .companion-content pre { background: #f8f9fa; padding: 15px; border-radius: 5px; overflow-x: auto; border-left: 4px solid #007acc; }
        .companion-content table { border: 1px solid #ddd; }
        .companion-content th, .companion-content td { border: 1px solid #ddd; }

        /* AsciiDoc admonition blocks (NOTE, TIP, WARNING, etc.) */
        .admonitionblock {
            margin: 1.5em 0;
            padding: 0.4em 0.6em;
            border-left: 4px solid;
            background: #f8f9fa;
            border-radius: 0 4px 4px 0;
        }

        .admonitionblock .title {
            font-weight: bold;
            text-transform: uppercase;
            font-size: 0.85em;
            margin-bottom: 0.25em;
            letter-spacing: 0.5px;
        }

        .admonitionblock .content {
            margin: 0;
        }

        /* Reduce spacing for paragraphs inside admonitions */
        .admonitionblock p {
            margin: 0.25em 0;
        }

        .admonitionblock p:first-child {
            margin-top: 0;
        }

        .admonitionblock p:last-child {
            margin-bottom: 0;
        }

        /* Specific admonition types */
        .admonitionblock.note {
            border-color: #17a2b8;
            background: #d1ecf1;
        }

        .admonitionblock.note .title {
            color: #0c5460;
        }

        .admonitionblock.tip {
            border-color: #28a745;
            background: #d4edda;
        }

        .admonitionblock.tip .title {
            color: #155724;
        }

        .admonitionblock.important {
            border-color: #ffc107;
            background: #fff3cd;
        }

        .admonitionblock.important .title {
            color: #856404;
        }

        .admonitionblock.warning,
        .admonitionblock.caution {
            border-color: #dc3545;
            background: #f8d7da;
        }

        .admonitionblock.warning .title,
        .admonitionblock.caution .title {
            color: #721c24;
        }
    </style>
</head>
<body>
    <div class="back-link">
        <a href="index.html">← Back to Layer Index</a>
    </div>

    <div class="header">
        <h1>image-rota</h1>
        <span class="badge">image</span>
        <span class="badge">v4.1.0</span>
        <p>Immutable GPT A/B layout for rotational OTA updates,
 boot/system redundancy, and a shared persistent data partition.</p>
    </div>

    
    <div class="section">
        <h2>Additional Documentation</h2>
        <div class="companion-content">
            <div class="sect1">
<h2 id="_immutable_root_and_mount_points">Immutable root and mount points</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This system uses an A/B, read‑only root with all writable state on a single persistent partition.</p>
</div>
<table class="tableblock frame-all grid-all stripes-even stretch">
<colgroup>
<col style="width: 11.1111%;">
<col style="width: 33.3333%;">
<col style="width: 11.1111%;">
<col style="width: 44.4445%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Mount point</th>
<th class="tableblock halign-left valign-top">Backing</th>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/dev/disk/by-slot/active/system</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">ext4</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Read‑only system root (active slot A or B)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/boot/firmware</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/dev/disk/by-slot/active/boot</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">vfat</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Boot files (active slot A or B)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/bootfs</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">BOOTFS</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">vfat</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Boot metadata</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/persistent</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">PERSISTENT</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">ext4</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Shared persistent storage</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/var</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/persistent/slots/&lt;slot&gt;/var</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">bind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Per‑slot runtime state (systemd, caches, etc.)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/home</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/persistent/home</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">bind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">User data shared across slots</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/var/log/journal</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/persistent/log/journal</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">bind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Single log directory used by both slots</p></td>
</tr>
</tbody>
</table>
<div class="ulist">
<ul>
<li>
<p><strong>Rationale (immutable root + A/B)</strong></p>
<div class="ulist">
<ul>
<li>
<p>Supports delta/incremental OTA updates by treating root as a static image.</p>
</li>
<li>
<p>Reliable rollbacks: slot can be flipped if a new root fails health checks.</p>
</li>
<li>
<p>Reduced write amplification and storage wear, clearer separation of state.</p>
</li>
<li>
<p>Predictable per‑slot state in <code>/var</code>.</p>
</li>
<li>
<p>Shared <code>/home</code> for slot agnostic user storage.</p>
</li>
<li>
<p>Shared journalling: centralised point for device logging.</p>
</li>
<li>
<p>Preserves SBOM accuracy: executing software matches the manifest exactly.</p>
</li>
<li>
<p>Blocks on-device package installs, preventing SBOM drift.</p>
</li>
<li>
<p>Enables auditable, reproducible releases and stronger supply-chain assurances.</p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>Logging</strong></p>
<div class="ulist">
<ul>
<li>
<p>A single persistent journal directory at <code>/persistent/log/journal</code> stores logs from either slot.</p>
</li>
<li>
<p>Journaling is configured for endurance and reliability.</p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>Machine Identity (systemd)</strong></p>
<div class="ulist">
<ul>
<li>
<p><code>/etc/machine-id</code> is synchronised early with <code>/persistent/common/etc/machine-id</code> using a oneshot unit.</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<div class="title">Warning</div>
</td>
<td class="content">
<div class="paragraph">
<p>Slot partition GPT labels are mandatory to associate the immutable root with its matching persistent storage.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Root slots must have stable PARTLABELs: e.g. <code>system_a</code> and <code>system_b</code>.</p>
</li>
<li>
<p>At boot, a generator reads the root slot’s <code>PARTLABEL</code> to select <code>/persistent/slots/&lt;slot&gt;/var</code>.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>If slot GPT labels are missing/duplicated, <code>/var</code> binding will fail.</p>
</div>
<div class="paragraph">
<p>If slot GPT labels can’t be guaranteed, this layout is not suitable for your device.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_slot_selection_run_time">Slot Selection (run-time)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Handled entirely by layer <code>rpi-ab-slot-mapper</code>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_slot_pairing_build_time">Slot Pairing (build-time)</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_definition">Definition</h3>
<div class="paragraph">
<p>This layer generates an image which has bit-for-bit identical A/B slot members:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Both <code>a.boot</code> and <code>b.boot</code> contain the exact same vfat filesystem.</p>
</li>
<li>
<p>Both <code>a.system</code> and <code>b.system</code> contain the exact same ext4 filesystem.</p>
</li>
<li>
<p>Filesystem UUIDs are intentionally identical across slots.</p>
</li>
<li>
<p>GPT partition GUIDs (PARTUUID) are unique per partition.</p>
</li>
<li>
<p>GPT partition labels (PARTLABEL) are unique per partition.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_rationale_and_benefits">Rationale and benefits</h3>
<div class="ulist">
<ul>
<li>
<p>Faster image creation: only a single slot pair is produced.</p>
</li>
<li>
<p>Simpler updates: only a single slot pair is built, tested, and distributed.</p>
</li>
<li>
<p>Update generation: operating on a single slot pair is unambiguous.</p>
</li>
<li>
<p>Smaller CI/storage footprint: fewer images to store.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_requirements_and_constraints">Requirements and constraints</h3>
<div class="ulist">
<ul>
<li>
<p>Does not use <code>UUID=</code> anywhere. Duplicate filesystem UUIDs make <code>/dev/disk/by-uuid/*</code> ambiguous.</p>
</li>
<li>
<p>Uses stable identifiers instead, e.g. <code>/dev/disk/by-slot/{active,other}/{boot,system}</code>.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_encrypted_luks_provisioning_map_layouts">Encrypted (LUKS) Provisioning Map layouts</h3>
<div class="ulist">
<ul>
<li>
<p>Identical content inside LUKS is supported. Slot mapping can use GPT labels or static map triplets to identify <code>mapper:&lt;name&gt;:&lt;part&gt;</code> slot components.</p>
</li>
</ul>
</div>
</div>
</div>
</div>

        </div>
    </div>
    

    
    <div class="section">
        <h2>Relationships</h2>
        
        <p><strong>Depends on:</strong></p>
        <div class="deps">
            <a href="image-base.html" class="dep-badge">image-base</a>
            <a href="device-base.html" class="dep-badge">device-base</a>
            <a href="rpi-ab-slot-mapper.html" class="dep-badge">rpi-ab-slot-mapper</a>
            <a href="systemd-min.html" class="dep-badge">systemd-min</a>
        </div>
        
        
        
        <p><strong>Provides:</strong> image</p>
        
        
    </div>
    

    
    <div class="section">
        <h2>Configuration Variables</h2>
        
        <p><strong>References:</strong>
        
        <code>IGconf_device_storage_type</code>
        
        </p>
        
        
        <p><strong>Declares</strong> (prefix: <code>image</code>):</p>
        <table>
            <thead>
                <tr>
                    <th>Variable</th>
                    <th>Description</th>
                    <th>Default</th>
                    <th>Validation</th>
                    <th>Policy</th>
                </tr>
            </thead>
            <tbody>
                
                <tr>
                    <td><code>IGconf_image_boot_part_size</code></td>
                    <td>Boot partition size per-slot.</td>
                    <td>
                       
                           
                           <code>96M</code>
                           
                       
                    </td>
                    <td>Size value with optional unit (bytes, k/m/g/s) or percentage</td>
                    <td>
                        <a href="variable-validation.html#set-policies" class="badge policy-immediate" title="Click for policy and validation help">immediate</a>
                    </td>
                </tr>
                
                <tr>
                    <td><code>IGconf_image_system_part_size</code></td>
                    <td>System partition size per-slot.</td>
                    <td>
                       
                           
                           <code>512M</code>
                           
                       
                    </td>
                    <td>Size value with optional unit (bytes, k/m/g/s) or percentage</td>
                    <td>
                        <a href="variable-validation.html#set-policies" class="badge policy-immediate" title="Click for policy and validation help">immediate</a>
                    </td>
                </tr>
                
                <tr>
                    <td><code>IGconf_image_data_part_size</code></td>
                    <td>Writable data partition retained across
 slot rotations.</td>
                    <td>
                       
                           
                           <code>1G</code>
                           
                       
                    </td>
                    <td>Size value with optional unit (bytes, k/m/g/s) or percentage</td>
                    <td>
                        <a href="variable-validation.html#set-policies" class="badge policy-immediate" title="Click for policy and validation help">immediate</a>
                    </td>
                </tr>
                
                <tr>
                    <td><code>IGconf_image_assetdir</code></td>
                    <td>Image specific asset directory</td>
                    <td>
                       
                           
                           <code>${DIRECTORY}</code>
                           
                       
                    </td>
                    <td>Non-empty string value</td>
                    <td>
                        <a href="variable-validation.html#set-policies" class="badge policy-immediate" title="Click for policy and validation help">immediate</a>
                    </td>
                </tr>
                
                <tr>
                    <td><code>IGconf_image_pmap</code></td>
                    <td>Provisioning Map type for this image layout.
 clear: All partitions will be provisioned unencrypted.
 crypt: All non-boot partitions will be provisioned encrypted.
 cryptslots: Only system OS partitions will be provisioned encrypted.
 cryptdata: Only the data partition will be provisioned encrypted.
 crypthybrid: B:system OS partition will be provisioned encrypted (dev only).</td>
                    <td>
                       
                           
                           <code>clear</code>
                           
                       
                    </td>
                    <td>Must be one of: clear, crypt, cryptslots, cryptdata, crypthybrid</td>
                    <td>
                        <a href="variable-validation.html#set-policies" class="badge policy-immediate" title="Click for policy and validation help">immediate</a>
                    </td>
                </tr>
                
                <tr>
                    <td><code>IGconf_image_ptable_protect</code></td>
                    <td>Enable eMMC Power-On Write Protect of the partition
 table. If enabled, the first 8MB of the boot storage device will be
 protected. Applies to eMMC only.</td>
                    <td>
                       
                           
                           <code class="long-default">$( [ "${IGconf_device_storage_type:-}" = "emmc" ] && echo y || echo n )</code>
                           
                       
                    </td>
                    <td>Non-empty string value</td>
                    <td>
                        <a href="variable-validation.html#set-policies" class="badge policy-immediate" title="Click for policy and validation help">immediate</a>
                    </td>
                </tr>
                
            </tbody>
        </table>
        
    </div>
    

    
    <div class="section">
        <h2>mmdebstrap</h2>

        

        

        

        
        
        <h3>Packages</h3>
        <p>Installs:</p>
        <ul>
            
            <li><code>dosfstools</code></li>
            
            <li><code>e2fsprogs</code></li>
            
        </ul>
        

        

        

        
    </div>
    

    <div class="section">
        <h2>Attributes</h2>
        <p><strong>File:</strong> <code>gpt/ab_userdata/image.yaml</code></p>
        
    </div>
</body>
</html>